#include "udp.hpp"
#include <vector>

using namespace std;
using namespace udpio;
UINT32 chackChar(const vector<char>& v) {
	UINT32 q = 0;
	size_t i;
	for (i = 0; i < v.size()-4; i += 4)
	{
		q ^= *(UINT32*)& v[i];
	}
	char t[4] = { 0 };
		if (i < v.size()) {
			for (UINT32 j = i; j <v.size(); j++) {
				t[j - i] =  v[j];
			}
			q ^= *(UINT32*)t;
	}
	return q;
}
UINT32 chackChar(const char* v,const size_t length) {
	UINT32 q = 0;
	size_t i;
	for (i = 0; i < length - 4; i += 4)
	{
		q ^= *(UINT32*)& v[i];
	}
	char t[4] = { 0 };
	if (i < length) {
		for (UINT32 j = i; j < length; j++) {
			t[j-i] = v[j];
		}
		q ^= *(UINT32*)t;
	}
	return q;
}


udpnet::udpnet(io_context& iox, ip::udp::endpoint ip_port) :datalist(256),countlist(256),udp(iox, ip_port)
{
	udp.async_receive_from(rebf,received_remote_endpoint, std::bind(&udpnet::ReceiveFunc, this));
}

void udpnet::connect(ip::udp::endpoint ip_port)
{
	udp.connect(ip_port);
}



void udpnet::send(const char * buf, UINT32 length, UINT8 mark)
{
	if (!sendable)return;//不可发送状态
	if (length > 33554432) axx("One message can not longer than 512*2^16(33554432) Bit");
	headmsg msg;
	UINT32 LL = length;
	msg.count = 0;
	msg.mark = mark;
	if (length > blockwidth) {
		msg.flag = 2;
	}
	else {
		msg.flag = 1;
	}
	const char* pn = buf;
	const headmsg* tp = &msg;
	array<const_buffers_1, 2> bf = {
		buffer(tp,sizeof(msg)),
		buffer(pn,0)
	};
	//char kc[blockwidth] = {0};
	while (length > blockwidth) {
		bf[1] = buffer(pn, blockwidth);
		msg.length = blockwidth;
		udp.async_send_to(bf, remote_endpoint, bind(&udpnet::SendedFunc, this));
		length -= blockwidth;

		pn += blockwidth;
		msg.count++;
	}

	if (length > 0) {
		msg.length = length;
		bf[1] = buffer(pn, length);
		udp.async_send_to(bf, remote_endpoint, bind(&udpnet::SendedFunc, this));
		msg.count++;
		
	}
	if (msg.flag == 2) {
		const UINT32 check = chackChar(buf, LL);
		bf[1]= buffer(&check,4);
		msg.flag = 3;
		//axx(msg.flag);
		msg.length = 4;

		udp.async_send_to(bf, remote_endpoint, bind(&udpnet::SendedFunc, this));
	}
	//axx(pn - buf);
}

void udpnet::send(const string buf, UINT8 mark)
{
	vector<char> tmp(buf.length()+1);
	tmp.assign(buf.data(), buf.data() + buf.length()+1);
	send(tmp.data(), tmp.size(), mark);
}

void udpnet::SetReceiveFunc(function<void(vector<char>& v, UINT8 mark,ip::udp::endpoint endpoint)> func)
{
	receivedFunc = func;
}

void udpnet::SetReceiveFunc(function<void(vector<char>& v, UINT8 mark)> func)
{
	receivedFunc = [&func](vector<char> & value, INT8 t_mark, ip::udp::endpoint endpoint) {
		func(value, t_mark);
	};
}

void udpnet::SendedFunc()
{

}

void udpnet::ReceiveFunc()
{
	
	//cout << "head:\n" <<"length:"<< (int)remsghead.length <<"\tmark:"<< (int)remsghead.mark<<"\tflag"<<(int)remsghead.flag<<"\tcount:" << remsghead.count << endl;
	//cout <<"body:"<< receivedbuffer << endl;
	//if(remsghead.flag==3){ axx("get"<< countlist[remsghead.mark]); }
	
	//axx(remsghead.count);
	if (remsghead.flag == 1) {
		datalist[remsghead.mark].assign(receivedbuffer, receivedbuffer + remsghead.length);
		if (receivedFunc!=NULL)receivedFunc(datalist[remsghead.mark], remsghead.mark, received_remote_endpoint);
	}
	if (remsghead.flag == 2 ){
		if(countlist[remsghead.mark] == remsghead.count) {
			if (remsghead.count == 0) {
				datalist[remsghead.mark].assign(receivedbuffer, receivedbuffer + remsghead.length);
			}
			else {
				datalist[remsghead.mark].insert(datalist[remsghead.mark].end(), receivedbuffer, receivedbuffer + remsghead.length);
			}
			countlist[remsghead.mark]++;
		}
		else {
			countlist[remsghead.mark] = 0;
		}
	}
#define printmsg(sg) std::cout<<sg<<std::endl
	if (remsghead.flag == 3 )
	{
		if (countlist[remsghead.mark] == remsghead.count){
			if(changePtrTotype(receivedbuffer,UINT32)== chackChar(datalist[remsghead.mark]))
			{
				if (receivedFunc != NULL)
					receivedFunc(datalist[remsghead.mark], remsghead.mark, received_remote_endpoint);
			}
			else
			{
				printmsg("校验失败");
			}
		}
		else printmsg("丢失");
		countlist[remsghead.mark] = 0;
	}

	udp.async_receive(rebf, std::bind(&udpnet::ReceiveFunc, this));
}

udpnet::~udpnet()
{
}
